import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;



/**
 * @modulename SymbolTable
 * @description This module generate the object of Type Symbol Table. This class
 *              is where the symbols parsed from the Source are stored. These
 *              symbols will be kept in tact so that they may be used in pass 2.
 * @callsequence SymbolTable symbolTableName = new SymbolTable();, where
 *               symbolTableName is the name of the SymbolTable object.
 * @inparam none
 * @outparam creates a new SymbolTable object
 * @errorstested tested through use with main
 * @errorsgenerated no known errors
 * @author Jamie Trefzger
 * @createdate 10/13/09
 * @modlog <p>
 *         <ul>
 *         Who/When/Why Travis, 10/17/09, edited output format 
 *         Nick, 10/23/09, added sortSymbolTable method and return functions
 *         </ul>
 *         Coding Standards Met: "Signed Code Leader(Jaron)"
 *         <p>
 *         Testing Standards Met: "Signed Test Leader(Jamie)"
 */
public class SymbolTable {
	protected static ArrayList<String> symbolArray;
	protected static ArrayList<String> locationAddressArray;
	protected static ArrayList<String> lengthArray;
	protected static ArrayList<String> relocationCodeArray;
	protected static ArrayList<String> usageArray;
	protected static ArrayList<String> equValueArray;

	public static void startSymbolTable() {
		symbolArray = new ArrayList<String>(0);
		locationAddressArray = new ArrayList<String>(0);
		lengthArray = new ArrayList<String>(0);
		relocationCodeArray = new ArrayList<String>(0);
		usageArray = new ArrayList<String>(0);
		equValueArray = new ArrayList<String>(0);
	}
	
	/**
	 * @modulename addSymbol
	 * @description Adds a symbol to the symbol table.
	 * @callsequence symbolTableName.addSymbol(symbol);, where symbolTableName
	 *               is the name of the SymbolTable object.
	 * @inparam (String) symbol, the symbol that you wish to add to the symbol
	 *          table.
	 * @outparam none
	 * @errorstested tested through use with main
	 * @errorsgenerated no known errors
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static void addSymbol(String symbol) {
		// a symbol is added everytime, no matter what. therefore, we
		// add an empty string to the other arrays to fill in the data for the
		// symbol given.
		symbolArray.add(symbol);
		locationAddressArray.add("");
		lengthArray.add("");
		relocationCodeArray.add("");
		usageArray.add("");
		equValueArray.add("");
	}

	/**
	 * @modulename editLocationAddress
	 * @description Edits the Location Address field for a symbol in the symbol
	 *              table.
	 * @callsequence symbolTableName.editLocationAddress(symbol,
	 *               locationAddress);, where symbolTableName is the name of the
	 *               SymbolTable object.
	 * @inparam (String) symbol, the symbol whose location address you want to
	 *          change. (String) locationAddress, The location address you are
	 *          modifying.
	 * @outparam none
	 * @errorstested tested by use with main
	 * @errorsgenerated no known errors
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static void editLocationAddress(String symbol, String locationAddress) {
		locationAddressArray.set(symbolArray.indexOf(symbol), locationAddress);
	}
	/**
	 * @modulename returnLocationAddress
	 * @description Returns the Location Address field for a symbol in the symbol
	 *              table.
	 * @callsequence symbolTableName.returnLocationAddress(symbol);, where symbolTableName is the name of the SymbolTable object.
	 * @inparam (String) symbol, the symbol whose location address you want
	 * @outparam (String) the location address of the symbol
	 * @errorstested tested via test module at bottom, a test symbol table was created with multiple test symbols and then fields of those symbols were returned
	 * @errorsgenerated no known errors

	 * @createdate 10/23/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String returnLocationAddress(String symbol){
		return locationAddressArray.get(symbolArray.indexOf(symbol));
	}
	/**
	 * @modulename editLength
	 * @description Edits the Length field for a symbol in the symbol table.
	 * @callsequence symbolTableName.editLength(symbol, length);, where
	 *               symbolTableName is the name of the SymbolTable object.
	 * @inparam (String) symbol, the symbol whose Length you want to change.
	 *          (String) length, The input for the Length you are modifying.
	 * @outparam none
	 * @errorstested tested by use with main
	 * @errorsgenerated no known errors
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static void editLength(String symbol, String length) {
		lengthArray.set(symbolArray.indexOf(symbol), length);
	}
	/**
	 * @modulename returnLength
	 * @description Returns the Length field for a symbol in the symbol table.
	 * @callsequence symbolTableName.returnLength(symbol);, where symbolTableName is the name of the SymbolTable object.
	 * @inparam (String) symbol, the symbol whose length you want
	 * @outparam (String) the length of the symbol
	 * @errorstested tested via test module at bottom, a test symbol table was created with multiple test symbols and then fields of those symbols were returned
	 * @errorsgenerated no known errors

	 * @createdate 10/23/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String returnLength(String symbol){
		return lengthArray.get(symbolArray.indexOf(symbol));
	}
	/**
	 * @modulename editRelocationCode
	 * @description Edits the Relocation Code field for a symbol in the symbol
	 *              table.
	 * @callsequence symbolTableName.editRelocationCode(symbol,
	 *               relocationCode);, where symbolTableName is the name of the
	 *               SymbolTable object.
	 * @inparam (String) symbol, the symbol whose Relocation Code you want to
	 *          change. (String) relocationCode, the input for the Relocation
	 *          Code you are modifying.
	 * @outparam none
	 * @errorstested tested by use with main
	 * @errorsgenerated no known errors
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static void editRelocationCode(String symbol, String relocationCode) {
		relocationCodeArray.set(symbolArray.indexOf(symbol), relocationCode);
	}
	/**
	 * @modulename returnRelocationCode
	 * @description Returns the Relocation Code for a symbol in the symbol table.
	 * @callsequence symbolTableName.returnRelocationCode(symbol);, where symbolTableName is the name of the SymbolTable object.
	 * @inparam (String) symbol, the symbol whose Relocation Code you want
	 * @outparam (String) the Relocation Code of the symbol
	 * @errorstested tested via test module at bottom, a test symbol table was created with multiple test symbols and then fields of those symbols were returned
	 * @errorsgenerated no known errors

	 * @createdate 10/23/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String returnRelocationCode(String symbol){
		return relocationCodeArray.get(symbolArray.indexOf(symbol));
	}
	/**
	 * @modulename editUsage
	 * @description Edits the Usage field for a symbol in the symbol table.
	 * @callsequence symbolTableName.editUsage(symbol, usage);, where
	 *               symbolTableName is the name of the SymbolTable object.
	 * @inparam (String) symbol, the symbol whose Usage you want to change.
	 *          (String) usage, the input for the Usage you are modifying.
	 * @outparam none
	 * @errorstested tested by use with main
	 * @errorsgenerated no known errors
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static void editUsage(String symbol, String usage) {
		usageArray.set(symbolArray.indexOf(symbol), usage);
	}
	/**
	 * @modulename returnUsage
	 * @description Returns the Usage for a symbol in the symbol table.
	 * @callsequence symbolTableName.returnUsage(symbol);, where symbolTableName is the name of the SymbolTable object.
	 * @inparam (String) symbol, the symbol whose Usage you want
	 * @outparam (String) the Usage of the symbol
	 * @errorstested tested via test module at bottom, a test symbol table was created with multiple test symbols and then fields of those symbols were returned
	 * @errorsgenerated no known errors

	 * @createdate 10/23/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String returnUsage(String symbol){
		return usageArray.get(symbolArray.indexOf(symbol));
	}

	/**
	 * @modulename editEQUValue
	 * @description Edits the EQU Value field for a symbol in the symbol table.
	 * @callsequence symbolTableName.editEQUValue(symbol, equValue);, where
	 *               symbolTableName is the name of the SymbolTable object.
	 * @inparam (String) symbol, the symbol whose EQU Value you want to change.
	 *          (String) equValue, the input for the EQU Value you are
	 *          modifying.
	 * @outparam none
	 * @errorstested tested by use with main
	 * @errorsgenerated no known errors
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static void editEQUValue(String symbol, String equValue) {
		equValueArray.set(symbolArray.indexOf(symbol), equValue);
	}
	/**
	 * @modulename returnEQUValue
	 * @description Returns the EQU Value for a symbol in the symbol table.
	 * @callsequence symbolTableName.returnEQUValue(symbol);, where symbolTableName is the name of the SymbolTable object.
	 * @inparam (String) symbol, the symbol whose EQU Value you want
	 * @outparam (String) the EQU Value of the symbol
	 * @errorstested tested via test module at bottom, a test symbol table was created with multiple test symbols and then fields of those symbols were returned
	 * @errorsgenerated no known errors

	 * @createdate 10/23/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String returnEQUValue(String symbol){
		return equValueArray.get(symbolArray.indexOf(symbol));
	}
	/**
	 * @modulename symbolTableSize
	 * @description Returns the number of symbols in the symbol table
	 * @callsequence int = symbolTableName.symbolTableSize();, where symbolTableName is the name of the SymbolTable object.
	 * @inparam none
	 * @outparam (int) size of symbol table
	 * @errorstested tested via test module at bottom, a test symbol table was created with multiple test symbols and then fields of those symbols were returned
	 * @errorsgenerated no known errors

	 * @createdate 10/23/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static int symbolTableSize(){
		return symbolArray.size();
	}
	/**
	 * @modulename symbolAtPosition
	 * @description Returns the symbol at a specific position
	 * @callsequence String = symbolTableName.symbolAtPosition(pos);, where symbolTableName is the name of the SymbolTable object.
	 * @inparam (int) pos, the position of the symbol you are looking for
	 * @outparam (string) the symbol at position pos
	 * @errorstested tested via test module at bottom, a test symbol table was created with multiple test symbols and then fields of those symbols were returned
	 * @errorsgenerated no known errors

	 * @createdate 10/23/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String symbolAtPosition(int pos){
		return symbolArray.get(pos);
	}
	/**
	 * @modulename symbolIsDefined
	 * @description Checks to see if the symbol given is already in the symbol
	 *              table.
	 * @callsequence Boolean = symbolTableName.symbolIsDefined(symbol);, where
	 *               symbolTableName is the name of the SymbolTable object.
	 * @inparam (String) symbol, the symbol you want to check the table for.
	 * @outparam A boolean that is true if the symbol exists in the table, false
	 *           otherwise.
	 * @errorstested tested by use with main
	 * @errorsgenerated no known errors
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static boolean symbolIsDefined(String symbol) {
		return symbolArray.contains(symbol);
	}

	/**
	 * @modulename outputSymbolTable
	 * @description Takes the data object of the symbol table and transforms it
	 *              into a tab deliminated string.
	 * @callsequence String = symbolTableName.outputSymbolTable();, where
	 *               symbolTableName is the name of the SymbolTable object.
	 * @inparam none
	 * @outparam The return value is a String in the format of a tab deliminated
	 *           symbol table.
	 * @errorstested tested by use with main
	 * @errorsgenerated no known errors
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String outputSymbolTable() {
		String symbolTable = new String();
		int i1 = -1;
		// set up the headers to the columns in the format we want.
		symbolTable = symbolTable + "Symbol";
		symbolTable = symbolTable + "\t\t\t|";
		symbolTable = symbolTable + "Location Address";
		symbolTable = symbolTable + "\t|";
		symbolTable = symbolTable + "Length";
		symbolTable = symbolTable + "\t\t\t|";
		symbolTable = symbolTable + "Relocation Code";
		symbolTable = symbolTable + "\t|";
		symbolTable = symbolTable + "Usage";
		symbolTable = symbolTable + "\t\t\t\t|";
		symbolTable = symbolTable + "EQU Value";
		symbolTable = symbolTable + "\t\r\n";

		// add each element of each column to the table 1 column at a time, with
		// a new lone
		// after all 6 columns have been output.
		while (i1 < (symbolArray.size() - 1)) {
			i1++;
			symbolTable = symbolTable + symbolArray.get(i1) + "\t";
			if (symbolArray.get(i1).length() < 15) {
				if (symbolArray.get(i1).length() < 8) {
					symbolTable = symbolTable + "\t";
				}
				symbolTable = symbolTable + "\t";
			}
			symbolTable = symbolTable + "LocAdd: ";
			symbolTable = symbolTable + locationAddressArray.get(i1);
			if (locationAddressArray.get(i1).length() < 15) {
				if (locationAddressArray.get(i1).length() < 8) {
					symbolTable = symbolTable + "\t";
				}
				symbolTable = symbolTable + "\t";
			}
			symbolTable = symbolTable + "Length: ";
			symbolTable = symbolTable + lengthArray.get(i1);
			if (lengthArray.get(i1).length() < 15) {
				if (lengthArray.get(i1).length() < 8) {
					symbolTable = symbolTable + "\t";
				}
				symbolTable = symbolTable + "\t";
			}
			symbolTable = symbolTable + "RelCode: ";
			symbolTable = symbolTable + relocationCodeArray.get(i1);
			if (relocationCodeArray.get(i1).length() < 15) {
				if (relocationCodeArray.get(i1).length() < 8) {
					symbolTable = symbolTable + "\t";
				}
				symbolTable = symbolTable + "\t";
			}
			symbolTable = symbolTable + "Usage: ";
			symbolTable = symbolTable + usageArray.get(i1);
			if (usageArray.get(i1).length() < 15) {
				if (usageArray.get(i1).length() < 8) {
					symbolTable = symbolTable + "\t";
				}
				symbolTable = symbolTable + "\t";
			}
			symbolTable = symbolTable + "\tValue: ";
			symbolTable = symbolTable + equValueArray.get(i1);
			symbolTable = symbolTable + "\r\n";
		}
		return symbolTable;
	}

	/**
	 * @modulename sortSymbolTable
	 * @description sorts the constructor arrays into alphabetical order
	 *              (ignoring case)
	 * @callsequence symbolTableName.sortSymbolTable();, where symbolTableName
	 *               is the name of the SymbolTable object.
	 * @inparam none
	 * @outparam none
	 * @errorstested tested via test module at the bottom of class. created a
	 *               test case symbol table that was not in alphabetical order.
	 *               i called the sorting method and then printed out the table.
	 *               it seems to work without errors.
	 * @errorsgenerated originally i was running into a problem because when i
	 *                  created the temporary unsorted array list i just say
	 *                  "tempSymbolArray = symbolArray" and this caused
	 *                  symbolArray to change it's ordering which caused lots
	 *                  more problems. this is why the temporary list at the
	 *                  begining is created in a non intuitive way. the problem
	 *                  has been fixed to the best of my knowlege.
	 * @author nicholas lister
	 * @createdate 10/23/09
	 * @modlog <p>
	 *         <ul>
	 *         Who/When/Why
	 *         </ul>
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 *         <p>
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static void sortSymbolTable() {
		// creat a temporary copy of the unsorted symbolArray as a List
		String[] tempSymbolArray = symbolArray.toArray(new String[0]);
		List<String> tempSymbolList = Arrays.asList(tempSymbolArray);

		// sort the temporary symbol list
		java.util.Collections.sort(tempSymbolList, new Comparator<String>() {
			// override the compare method
			public int compare(String symbol1, String symbol2) {
				// order alphabetacally (ignoring case)
				return symbol1.compareToIgnoreCase(symbol2);
			}

		});

		// create temporary constructor arrays
		ArrayList<String> sortedSymbolArray = new ArrayList<String>(0);
		ArrayList<String> sortedLocationAddressArray = new ArrayList<String>(0);
		ArrayList<String> sortedLengthArray = new ArrayList<String>(0);
		ArrayList<String> sortedRelocationCodeArray = new ArrayList<String>(0);
		ArrayList<String> sortedUsageArray = new ArrayList<String>(0);
		ArrayList<String> sortedEquValueArray = new ArrayList<String>(0);

		// fill the temporary constructor arrays with the appropriate values
		for (int i = 0; i < tempSymbolList.size(); i++) {
			// get the current symbol from the sorted list
			String symbol = tempSymbolList.get(i);

			// add the current symbol to the sorted symbol array
			sortedSymbolArray.add(symbol);

			// find the index of the current symbol in the non sorted arrays
			int symbolIndex = symbolArray.indexOf(symbol);

			// get the field values for the current symbol from the non sorted
			// array
			sortedLocationAddressArray.add(locationAddressArray
					.get(symbolIndex));
			sortedLengthArray.add(lengthArray.get(symbolIndex));
			sortedRelocationCodeArray.add(relocationCodeArray.get(symbolIndex));
			sortedUsageArray.add(usageArray.get(symbolIndex));
			sortedEquValueArray.add(equValueArray.get(symbolIndex));
		}

		// replace constructor arrays with sorted arrays
		symbolArray = sortedSymbolArray;
		locationAddressArray = sortedLocationAddressArray;
		lengthArray = sortedLengthArray;
		relocationCodeArray = sortedRelocationCodeArray;
		usageArray = sortedUsageArray;
		equValueArray = sortedEquValueArray;
	}

	// sorting tester
	@SuppressWarnings("static-access")
	public static void main(String[] args) {

		// create a test symbol table with symbols added in NON alphabetical order. all the fields for the symbol are equal to the symbol name so i can tell that they match up in the end. 
		SymbolTable symTab = new SymbolTable();

		symTab.addSymbol("XyZ0123");
		symTab.editLocationAddress("XyZ0123", "XyZ0123");
		symTab.editLength("XyZ0123", "XyZ0123");
		symTab.editRelocationCode("XyZ0123", "XyZ0123");
		symTab.editUsage("XyZ0123", "XyZ0123");
		symTab.editEQUValue("XyZ0123", "XyZ0123");
		
		symTab.addSymbol("test_case");
		symTab.editLocationAddress("test_case", "test_case");
		symTab.editLength("test_case", "test_case");
		symTab.editRelocationCode("test_case", "test_case");
		symTab.editUsage("test_case", "test_case");
		symTab.editEQUValue("test_case", "test_case");
		
		symTab.addSymbol("=10");
		symTab.editLocationAddress("=10", "=10");
		symTab.editLength("=10", "=10");
		symTab.editRelocationCode("=10", "=10");
		symTab.editUsage("=10", "=10");
		symTab.editEQUValue("=10", "=10");

		symTab.addSymbol("ABC");
		symTab.editLocationAddress("ABC", "ABC");
		symTab.editLength("ABC", "ABC");
		symTab.editRelocationCode("ABC", "ABC");
		symTab.editUsage("ABC", "ABC");
		symTab.editEQUValue("ABC", "ABC");

		symTab.addSymbol("Label-Name");
		symTab.editLocationAddress("Label-Name", "Label-Name");
		symTab.editLength("Label-Name", "Label-Name");
		symTab.editRelocationCode("Label-Name", "Label-Name");
		symTab.editUsage("Label-Name", "Label-Name");
		symTab.editEQUValue("Label-Name", "Label-Name");

		symTab.addSymbol("abc");
		symTab.editLocationAddress("abc", "abc");
		symTab.editLength("abc", "abc");
		symTab.editRelocationCode("abc", "abc");
		symTab.editUsage("abc", "abc");
		symTab.editEQUValue("abc", "abc");

		// show the symbol table in non alphabetacal order
		System.out.print(symTab.outputSymbolTable());
		System.out.println();
		
		// sort the test symbol table
		symTab.sortSymbolTable();

		// check to see if the symbol table is now in alphabetical order
		System.out.print(symTab.outputSymbolTable());
	
	}
}
